package com.digua.iot.confignetwork.entitiy

import java.util.Random

class AirKissEncoder(ssid: String, password: String) {
    private val mEncodedData = IntArray(2 shl 14)
    private var mLength = 0

    // Random char should be in range [0, 127).
    private val mRandomChar = Random().nextInt(0x7F).toChar()

    init {
        var times = 5
        while (times-- > 0) {
            leadingPart()
            magicCode(ssid, password)
            for (i in 0..14) {
                prefixCode(password)
                val data = password + mRandomChar + ssid
                var content = ByteArray(4)
                var index = 0
                while (index < data.length / 4) {
                    System.arraycopy(data.toByteArray(), index * 4, content, 0, content.size)
                    sequence(index, content)
                    ++index
                }
                if (data.length % 4 != 0) {
                    content = ByteArray(data.length % 4)
                    System.arraycopy(data.toByteArray(), index * 4, content, 0, content.size)
                    sequence(index, content)
                }
            }
        }
    }

    fun getEncodedData() = mEncodedData.copyOf(mLength)

    private fun appendEncodedData(length: Int) {
        mEncodedData[mLength++] = length
    }

    private fun getCrc8(data: ByteArray): Int {
        var len = data.size
        var i = 0
        var crc: Byte = 0x00
        while (len-- > 0) {
            var extract = data[i++]
            for (tempI in 8 downTo 1) {
                var sum = (crc.toInt() and 0xFF xor (extract.toInt() and 0xFF)).toByte()
                sum = (sum.toInt() and 0xFF and 0x01).toByte()
                crc = (crc.toInt() and 0xFF ushr 1).toByte()
                if (sum.toInt() != 0) {
                    crc = (crc.toInt() and 0xFF xor 0x8C).toByte()
                }
                extract = (extract.toInt() and 0xFF ushr 1).toByte()
            }
        }
        return crc.toInt() and 0xFF
    }

    private fun getCrc8(stringData: String) = getCrc8(stringData.toByteArray())

    private fun leadingPart() {
        for (i in 0..49) {
            for (j in 1..4) appendEncodedData(j)
        }
    }

    private fun magicCode(ssid: String, password: String) {
        val length = ssid.length + password.length + 1
        val magicCode = IntArray(4)
        magicCode[0] = 0x00 or (length ushr 4 and 0xF)
        if (magicCode[0] == 0) magicCode[0] = 0x08
        magicCode[1] = 0x10 or (length and 0xF)
        val crc8 = getCrc8(ssid)
        magicCode[2] = 0x20 or (crc8 ushr 4 and 0xF)
        magicCode[3] = 0x30 or (crc8 and 0xF)
        for (i in 0..19) {
            for (j in 0..3) appendEncodedData(magicCode[j])
        }
    }

    private fun prefixCode(password: String) {
        val length = password.length
        val prefixCode = IntArray(4)
        prefixCode[0] = 0x40 or (length ushr 4 and 0xF)
        prefixCode[1] = 0x50 or (length and 0xF)
        val crc8 = getCrc8(byteArrayOf(length.toByte()))
        prefixCode[2] = 0x60 or (crc8 ushr 4 and 0xF)
        prefixCode[3] = 0x70 or (crc8 and 0xF)
        for (j in 0..3) appendEncodedData(prefixCode[j])
    }

    private fun sequence(index: Int, data: ByteArray) {
        val content = ByteArray(data.size + 1)
        content[0] = (index and 0xFF).toByte()
        System.arraycopy(data, 0, content, 1, data.size)
        val crc8 = getCrc8(content)
        appendEncodedData(0x80 or crc8)
        appendEncodedData(0x80 or index)
        for (aData in data) appendEncodedData(aData.toInt() or 0x100)
    }
}